home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
090
/
pctj8409.arc
/
X2OKI.ASM
< prev
Wrap
Assembly Source File
|
1986-09-14
|
12KB
|
335 lines
;**********************************************************************************
;* This program traps the printer output interrupt (17H), and translates outgoing *
;* IBM-style line drawing characters to codes compatible with the Okidata 84p. *
;* *
;* Since the 84p doesn't fully implement all double-line characters, we translate *
;* everything into single-line graphics. Other graphics characters (hearts, *
;* happy faces, etc.) are not supported in this program. *
;* *
;* The utility should be disabled prior to running any program which depends *
;* on the Okidata graphics character set or uses the Okigraph graphics printing *
;* facility (such as Lotus 1-2-3). *
;* *
;* USE: Giving the command "X2OKI" for the first time will install the *
;* conversion utility, and report "X2Oki installed and running." *
;* Thereafter, giving the "X2Oki" command will toggle the translation *
;* feature off and on, reporting "X2Oki temporarily deactivated" or *
;* "X2Oki reactivated." *
;* *
;* *
;* Copyright (C) 1983 Jeffrey P. Garbers. All rights reserved. *
;**********************************************************************************
CSEG segment para public 'CODE'
assume cs:CSEG, ds:CSEG
; The printer interrupt number is defined by the PC to be 17H.
PRINTER_INTERRUPT equ 17H
; The signature word is an arbitrary 16-bit number that we'll use in
; checking to see if the utility has already been installed.
SIGNATURE equ 0944H
; DOS is a handy macro that calls DOS for service. Its first argument
; is a function code number. The second one (if it's there) is assumed
; to be an offset which needs to be loaded into DX.
DOS macro fcn_code, location_arg
mov ah, fcn_code
ifnb <location_arg>
mov dx, offset location_arg
endif
int 21H
endm
; A couple of DOS function codes, defined.
PRINT_MESSAGE equ 09H
SET_VECTOR equ 25H
; Programs to be passed through the EXE2BIN utility need to start at
; address 100H.
org 100H ; goes thru EXE2BIN
; Since PC-DOS will always start us at 100H, we need to do a jump right
; away to get around the data and interrupt-handling stuff, and get
; right to the program initialization.
HOME: jmp START ; skip around all the data space
; Data areas for X2OKI.
; BIOS_HANDLER contains the doubleword address which used to be the
; printer interrupt handler. We leave here through the old vector, so any
; other interceptors that may have been installed (spoolers, etc.) will
; still work properly.
BIOS_HANDLER dd ? ; address of former int handler
; The translation tables. The first table (PC_CHARS) contains the
; IBM-defined codes for the single and double line box drawing characters.
; The parallel second table (OKI_CHARS) contains the Okidata 84p codes
; for those characters. Notice that the lines of the OKI_CHARS table
; are quite similar; this is because PC double-line characters are translated
; to Oki single-liners as mentioned in the introduction. NUMBER_OF_CHARS
; just lets the assembler figure out how many of these we have to look thru.
; In order to expand the conversion tables, just add codes to both tables
; as needed, making sure that you keep the order straight.
PC_CHARS equ this byte
db 218,194,191,195,180,192,193,217,196,179,197 ; sng boxes
db 201,203,187,204,185,200,202,188,205,186,206 ; dbl boxes
db 214,210,183,199,182,211,208,189,215 ; combinations
db 213,209,184,198,181,212,207,190,216 ; more combos
NUMBER_OF_CHARS equ $-PC_CHARS
OKI_CHARS equ this byte
db 152,145,153,147,146,154,144,155,149,150,143
db 152,145,153,147,146,154,144,155,149,150,143
db 152,145,153,147,146,154,144,155,143
db 152,145,153,147,146,154,144,155,143
; An area to save the incoming character, which we need to preserve.
INCOMING_CHAR db ?
;************************************************************************
; START OF X2Oki CODE
;************************************************************************
MAIN proc far
; The lodged part. This translates Oki chars to our chars.
HANDLER: jmp short HANDLE1 ; skip data
; Don't change that jump, or put anything between here and the INSTALLED
; and ACTIVE variables. Later we assume that they come right after
; the start of the handler.
; The INSTALLED variable is just an instance of the signature word. We
; can check for the presence of this word to see if the driver has
; already been installed.
INSTALLED dw SIGNATURE
; ACTIVE is 1 if the utility is active. If it's zero, we just pass
; characters right through and don't do any conversion.
ACTIVE db 1 ; we are active
; Watch for the use of the CS: override prefixes in here. When we get
; control, the only segment register we know about is CS (the Code
; Segment), so we'll use it to check to see if we're running.
HANDLE1:
mov cs:INCOMING_CHAR, al ; save incoming character
; There are three cases in which we want to do nothing here and just
; skip right out to the HANDLEdone location (which passes control
; off to the old BIOS handler). Those are:
;
; (1) Utility not currently active.
cmp cs:ACTIVE, 1 ; are
jnz HANDLEdone ; we're not running
; (2) Request to BIOS is not a "please print character" call,
; but rather a status or other request.
or ah, ah
jnz HANDLEdone ; not a print call
; (3) The character to be printed has a value below 128, and
; therefore can't be a line drawing character. Testing
; for this allows us to pass most of the printing characters
; through quickly without having to scan the table.
test al, 80H
jz HANDLEdone ; not a graphics char
; Okay, we have a character we may wish to do something to. First, let's
; save a few registers.
pushf ; need to save this because
push es ; we're going to mess with
push cx ; the direction flag during
push di ; our scanning.
; The following two-instruction sequence gets a copy of CS into the ES
; register. We will be scanning the table which exists in this segment.
push cs
pop es
; When scanning a table, you must:
; (1) Point ES:DI to the start of the table (we've already done ES)
mov di, offset PC_CHARS
; (2) Set CX to the size of the table
mov cx, NUMBER_OF_CHARS
; (3) Set or clear the direction flag to indicate the direction
; of the scan (normally CLD)
cld
; Now everything is set up for the scan. Let's use the nice 8088/8086
; instructions to do it. The one we're going to use will scan bytes
; until it finds a match, or until it runs out of table. The REPNZ
; prefix means "repeat this instruction while there isn't a match".
repnz scasb ; look it up
jnz HANDLEscanDone ; couldn't find it
; Now DI points one past the matching entry (remember that DI is always
; adjusted, even if we find a match. We now need to convert DI
; into an offset into the OKI_CHARS table. Since we started DI at
; the offset of PC_CHARS, and since we're one beyond our match,
; we can subtract one more than our starting spot to find our offset
; into the OKI_CHARS table. Messy? A little. Work it out on paper if
; you have trouble picking up on this notion.
sub di, offset PC_CHARS+1 ; make it an offset
mov al, es:OKI_CHARS[di]
; Okay, either we've translated the character or we don't know how to translate
; it. Recover the registers we saved, and split out to the original
; BIOS handler.
HANDLEscanDone:
pop di
pop cx
pop es
popf
; Notice again the use of the CS override here. We have not used the
; DS register at all during this handler.
HANDLEdone: pushf ; must PUSH before manually calling
call cs:BIOS_HANDLER ; an interrupt handler
mov al, cs:INCOMING_CHAR ; recover original character codes
iret ; and return to caller
; Since we're going to be doing an end-but-stay-resident, we'll need to
; mark where the handler part ends. The label LAST will serve nicely.
LAST equ this byte ; last part of resident
;************************************************************************
; TRANSIENT PART of X2Oki
;
; See text of article about the difference between the resident
; and transient parts of an interceptor program.
;
;************************************************************************
START: push ds
xor ax, ax
push ax ; .COM programs start like this
; AX is already zero, and we want to look at an interrupt handler's address
; (which lives in segment 0000). So let's just move our zeroed AX into
; ES for snooping purposes. We'll set SI to 17H (the printer service
; interrupt) times 4 (the number of bytes in each handler address).
mov es, ax
mov si, PRINTER_INTERRUPT*4 ; point to installed address
; First, we'll get the address that's there so we can call it later.
lods word ptr es:[si]
mov word ptr BIOS_HANDLER, ax
lods word ptr es:[si]
mov word ptr BIOS_HANDLER+2, ax
; We've now got the old handler in BIOS_HANDLER. Pick it up as a 32-bit
; pointer so we can see who's there.
les si, BIOS_HANDLER
; We know that if it's our handler, there's the SIGNATURE word two bytes
; beyond the beginning of the handler (our handler starts with a short
; jump). See if it's us, and if it isn't, install us.
cmp word ptr es:[si+2], SIGNATURE ; this us?
jnz INSTALL ; not yet-- install us
; Toggle the on/off setting. The ACTIVE flag lives four bytes beyond the
; handler-- let's flop its setting.
mov al, es:[si+4] ; pick up running flag
xor al, 1 ; flip the bit
mov es:[si+4], al ; replace it
; Okay, we now have AL as the new state. Give a message saying
; our current state.
mov dx, offset m$RUNNING ; for now, assume it's running
cmp al, 1 ; were we right?
jz MSG_N_SPLIT ; yes, take this
mov dx, offset m$OFFNOW ; report "off"
; When we get here, DX is pointing to a status message. Print it and
; leave.
MSG_N_SPLIT: DOS PRINT_MESSAGE ; "print message" function
ret ; goodbye!
; If we get to this spot, it's the first time that the program has been
; run. Install our own handler into the interrupt vector, report this
; event with a message, and let DOS know we want to stay around.
INSTALL:
mov al, PRINTER_INTERRUPT ; printer interrupt, please
DOS SET_VECTOR HANDLER ; DOS function 25h is "set interrupt"
DOS PRINT_MESSAGE m$NOWREADY
; Everything's complete. Leave the program via INT 27H with DX pointing to
; the end of the resident part, and DOS'll keep the important stuff around
; while freeing up the memory occupied by the part that's not part of
; the interrupt handler per se.
mov dx, offset LAST+1 ; point to the end of the fixed part
int 27H ; end-but-stick-around
; Three status report messages.
m$NOWREADY db "X2Oki installed and running.$"
m$RUNNING db "X2Oki reactivated.$"
m$OFFNOW db "X2Oki temporarily deactivated.$"
MAIN endp
CSEG ends
end HOME